// 下载文件

package internal

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

type Writer struct {
	aim        *Aim
	startPos   int64         // 下载游标开始位置
	endPos     int64         // 下载游标截止位置
	fileName   string        // 文件名
	writerNum  chan int64    // 下载数据长度
	quitSignal chan struct{} // 退出协程信号
}

func NewWriter(startPos, endPos int64, fileName string, writerNum chan int64, quit chan struct{}, aim *Aim) *Writer {
	return &Writer{
		startPos:   startPos,
		endPos:     endPos,
		fileName:   fileName,
		writerNum:  writerNum,
		quitSignal: quit,
		aim:        aim,
	}
}

// 存储数据
func (w *Writer) store() {
	read := w.getClient()

	defer func() {
		_ = read.Close()
		w.quitSignal <- struct{}{}
	}()

	// 读取buff
	reader := bufio.NewReader(read)

	f, err := os.Create(w.fileName)
	if err != nil {
		log.Fatal(err)
		return
	}

	write := bufio.NewWriter(f)
	defer func() {
		write.Flush()
	}()

	buf := make([]byte, 1024)
	var schedule int64
	success := make(chan struct{})

	// 写入文件
	go func() {
		defer func() {
			success <- struct{}{}
		}()

		for {
			// 读取句柄中的数据到buf
			lr, err := reader.Read(buf)
			if err != nil {
				// 读取数据完成
				if err == io.EOF {
					return
				}
				log.Fatal(err)
			}

			if lr > 0 {
				// 数据写入存储文件buf
				lw, err := write.Write(buf[:lr])
				if err != nil {
					log.Fatal(err)
				}

				if lw != lr {
					log.Fatal(io.ErrShortWrite)
				}

				// 写入数据长度
				schedule += int64(lw)
			}
		}
	}()

	// 时间滚动返回进度
	spaceTime := 200 * time.Millisecond
	ticker := time.NewTicker(spaceTime)
	for {
		select {
		case <-ticker.C:
			w.writerNum <- schedule
			schedule = 0
		case <-success:
			return
		}
	}
}

// 获取下载句柄
func (w *Writer) getClient() io.ReadCloser {
	client := http.DefaultClient
	client.Timeout = time.Hour

	req, err := http.NewRequest(http.MethodGet, w.aim.fileUrl, nil)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", w.startPos, w.endPos))
	req.Header.Set("User-Agent", "Citrix-Receiver")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}

	if resp.ContentLength <= 0 {
		log.Fatal("[*] Destination server does not support breakpoint download.")
	}
	if resp.ContentLength < w.endPos-w.startPos || resp.ContentLength < 1024 {
		all, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}
		log.Fatal("服务端返回数据异常，请重试！！ ===> ", string(all))
	}

	return resp.Body
}
